package com.katesoft.scale4j.persistent.spring.postprocessor;

import static com.katesoft.scale4j.common.spring.IPostProcessingOrder.DATASOURCE_POST_PROCESSOR_PRIORITY;

import java.util.HashMap;
import java.util.Map;

import javax.sql.XADataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.Ordered;

import com.katesoft.scale4j.common.services.IBeanNameReferences;
import com.katesoft.scale4j.common.spring.AbstractBeanPropertiesOverridePostProcessor;
import com.katesoft.scale4j.common.utils.AssertUtility;

/**
 * The main reason for creating this class is to allow connection details overrides for
 * {@link XADataSource} without changing spring configuration files.
 * <p/>
 * Also this class provides unified solution for overriding datasource bean properties for both
 * {@link BasicDataSource} and {@link com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean} - this is
 * necessary because bean's property names don't match.
 * <p/>
 * <p/>
 * 
 * @author kate2007
 */
public class DatasourcePostProcessor extends AbstractBeanPropertiesOverridePostProcessor implements
         Ordered {
   private String user;
   private String password;
   private String url;
   private String driverClass;
   private int poolInitialSize = -1;
   private int poolMaxSize = -1;
   private String uniqueResourceName;

   public DatasourcePostProcessor() {
      setTargetBean(IBeanNameReferences.ACTUAL_DATASOURCE);
      setOrder(DATASOURCE_POST_PROCESSOR_PRIORITY);
   }

   @Override
   protected void doPostProcessing(
            @SuppressWarnings("unused") ConfigurableListableBeanFactory configurableListableBeanFactory) {
      BeanDefinition beanDefinition = getBeanDefinition();
      String beanClassName = beanDefinition.getBeanClassName();
      Map<Object, Object> newBeanProperties = new HashMap<Object, Object>();
      if (super.beanProperties != null) {
         newBeanProperties.putAll(super.beanProperties);
      }
      if (beanClassName.equalsIgnoreCase("com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean")) {
         logger.info("overriding XA datasource bean definition[id=%s, class=%s]", getTargetBean(),
                  beanClassName);
         if (user != null) {
            newBeanProperties.put("user", user);
         }
         if (poolInitialSize > 0) {
            newBeanProperties.put("minPoolSize", poolInitialSize);
         }
         if (poolMaxSize > 0) {
            newBeanProperties.put("maxPoolSize", poolMaxSize);
         }
         if (uniqueResourceName != null) {
            newBeanProperties.put("uniqueResourceName", uniqueResourceName);
         }
      } else if (beanClassName.equalsIgnoreCase(BasicDataSource.class.getName())) {
         logger.info("overriding basic datasource bean definition[id=%s, class=%s]",
                  getTargetBean(), beanClassName);
         if (user != null) {
            newBeanProperties.put("username", user);
         }
         if (poolInitialSize > 0) {
            newBeanProperties.put("initialSize", poolInitialSize);
         }
         if (poolMaxSize > 0) {
            newBeanProperties.put("maxActive", poolMaxSize);
         }
      }
      //
      if (password != null) {
         newBeanProperties.put("password", password);
      }
      if (url != null) {
         newBeanProperties.put("url", url);
      }
      if (driverClass != null) {
         newBeanProperties.put("driverClassName", driverClass);
      }
      //
      super.beanProperties = newBeanProperties;
      logger.info("new datasource bean properties = %s", newBeanProperties);
      overrideBeanProperties();
   }

   public void setUser(String user) {
      this.user = user;
   }

   public void setPassword(String password) {
      this.password = password;
   }

   public void setUrl(String url) {
      this.url = url;
   }

   public void setDriverClass(Class<?> driverClass) {
      this.driverClass = driverClass.getName();
   }

   public void setPoolInitialSize(int poolInitialSize) {
      AssertUtility.assertTrue(poolInitialSize > 0, "initial pool size must be positive");
      this.poolInitialSize = poolInitialSize;
   }

   public void setPoolMaxSize(int poolMaxSize) {
      AssertUtility.assertTrue(poolMaxSize > 0, "max pool size must be positive");
      this.poolMaxSize = poolMaxSize;
   }

   /**
    * set the unique name of XA datasource(it is possible to have few datasource in context and it
    * is required by Atomikos to set unique names of each).
    * 
    * @param uniqueResourceName
    *           xa datasource resource name
    * @see com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean#setUniqueResourceName(String)
    */
   public void setUniqueResourceName(String uniqueResourceName) {
      this.uniqueResourceName = uniqueResourceName;
   }
}
